package com.k2data.k2app.service;

import com.google.common.base.Objects;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.k2data.k2app.common.K2Assert;
import com.k2data.k2app.common.MyConstant;
import com.k2data.k2app.common.OrikaMapperFactory;
import com.k2data.k2app.common.ResponseConstant;
import com.k2data.k2app.constant.Constant;
import com.k2data.k2app.domain.*;
import com.k2data.k2app.domain.po.*;
import com.k2data.k2app.domain.po.v1.Contacts;
import com.k2data.k2app.domain.po.v1.RCustomerContacts;
import com.k2data.k2app.domain.po.v1.RDeviceContacts;
import com.k2data.k2app.domain.po.v1.RFile;
import com.k2data.k2app.exception.K2ResponseException;
import com.k2data.k2app.mapper.*;
import com.k2data.k2app.rest.InstanceClient;
import com.k2data.k2app.rest.SysClient;
import com.k2data.k2app.rest.domain.DictRest;
import com.k2data.k2app.rest.domain.DictResult;
import com.k2data.k2app.rest.domain.RectList;
import com.k2data.k2app.utils.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.nio.file.Paths;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.stream.Collectors;

/**
 * @author lidong9144@163.com 17-3-17.
 */
@Service
public class DeviceService {

    private final InstanceClient instanceClient;

    private final DeviceMapper deviceMapper;
    private final RDeviceTemplateDeviceMapper rDeviceTemplateDeviceMapper;
    private final TemplateService templateService;
    private final DeviceBomMapper deviceBomMapper;
    private final RDeviceMonitorPointMapper rDeviceMonitorPointMapper;
    private final CustomerMapper customerMapper;
    private final RDeviceCustomerMapper rDeviceCustomerMapper;
    private final TDeviceMapper tDeviceMapper;
    private final KmxService kmxService;
    private final DeviceCustomColumnMapper deviceCustomColumnMapper;
    private final TRDeviceMonitorPointMapper trDeviceMonitorPointMapper;

    @Autowired
    private RFileMapper rFileMapper;
    @Autowired
    private ContactsMapper contactsMapper;
    @Autowired
    private RDeviceContactsMapper rDeviceContactsMapper;
    @Autowired
    private MonitorService monitorService;
    @Autowired
    private SysClient sysClient;
    @Autowired
    public DeviceService(DeviceMapper deviceMapper,
                         RDeviceTemplateDeviceMapper rDeviceTemplateDeviceMapper, TemplateService templateService,
                         DeviceBomMapper deviceBomMapper,
                         RDeviceMonitorPointMapper rDeviceMonitorPointMapper, CustomerMapper customerMapper,
                         RDeviceCustomerMapper rDeviceCustomerMapper, TDeviceMapper tDeviceMapper,
                         KmxService kmxService, DeviceCustomColumnMapper deviceCustomColumnMapper,
                         InstanceClient instanceClient, TRDeviceMonitorPointMapper trDeviceMonitorPointMapper) {
        this.deviceMapper = deviceMapper;
        this.rDeviceTemplateDeviceMapper = rDeviceTemplateDeviceMapper;
        this.templateService = templateService;
        this.deviceBomMapper = deviceBomMapper;
        this.rDeviceMonitorPointMapper = rDeviceMonitorPointMapper;
        this.customerMapper = customerMapper;
        this.rDeviceCustomerMapper = rDeviceCustomerMapper;
        this.tDeviceMapper = tDeviceMapper;
        this.kmxService = kmxService;
        this.deviceCustomColumnMapper = deviceCustomColumnMapper;
        this.instanceClient = instanceClient;
        this.trDeviceMonitorPointMapper = trDeviceMonitorPointMapper;
    }

    @Transactional
    public int update(Device po) throws K2ResponseException {
        // 更新状态
        if (po.getStatus() != null) {
            DeviceCond cond = new DeviceCond();
            cond.setDeviceId(po.getId());
            Device device = deviceMapper.selectDevice(cond);

            // 如果状态改变, 就插入一条状态改变历史
            if (!Objects.equal(device.getStatus(), po.getStatus())) {
                deviceMapper.insertStatusLog(device.getId(), device.getStatus(), po.getStatus(), "");
            }
        }

        po.setTDeviceId(null);  // 不允许更新
        int num = deviceMapper.updateByPrimaryKeySelective(po);


        if (num == 0) {
            throw new K2ResponseException(ResponseConstant.ID_NOT_FOUND,
                    String.format("%s(id: %s)", ResponseConstant.MSG_DEVICE_ID_NOT_FOUND, po.getId()));
        }

        if (!StringUtils.isBlank(po.getDataSource())) {
            DeviceCustomColumn deviceCustomColumnCond = new DeviceCustomColumn();
            deviceCustomColumnCond.setDeviceId(po.getId());
            deviceCustomColumnCond.setName("dataSource");
            deviceCustomColumnCond.setNameEn("dataSource");

            DeviceCustomColumn deviceCustomColumn = deviceCustomColumnMapper.selectOne(deviceCustomColumnCond);

            if (deviceCustomColumn != null) {
                deviceCustomColumn.setValue(po.getDataSource());
                deviceCustomColumnMapper.updateByPrimaryKeySelective(deviceCustomColumn);
            } else {
                deviceCustomColumnCond.setValue(po.getDataSource());
                deviceCustomColumnMapper.insertSelective(deviceCustomColumnCond);
            }
        }

        // 关联客户
        if (po.getCustomerId() != null) {
            K2Assert.existRelationCustomerId(po.getCustomerId());

            RDeviceCustomer rPo = new RDeviceCustomer();
            rPo.setDeviceId(po.getId());
            rPo.setCustomerId(po.getCustomerId());

            if (Constant.DELETE_TABLE_RELATION.equals(po.getCustomerId())) {
                rDeviceCustomerMapper.delete(rPo);
            } else {
                if (rDeviceCustomerMapper.updateCustomerIdByDeviceId(po.getCustomerId(), po.getId()) == 0) {
                    rDeviceCustomerMapper.insertSelective(rPo);
                }
            }
        }

        rFileMapper.deleteByDeviceId(po.getId(),"device");
        List<RFile> rFiles = RFile.updateFiles(po.getId(),po.getFileList(),"device");
        if(rFiles != null && rFiles.size()>0) {
            rFileMapper.insertBatch(rFiles);
        }

        // 联系人
        rDeviceContactsMapper.deleteByDeviceId(po.getId());
        if(po.getContacts() != null && po.getContacts().size()>0){
            for(Contacts contactse: po.getContacts()){
                RDeviceContacts rDeviceContacts = new RDeviceContacts();
                rDeviceContacts.setDeviceId(po.getId());
                rDeviceContacts.setCustomerId(po.getCustomerId());
                // 判断联系人是否是自己添加的
                if(!StringUtils.isEmpty(contactse.getIsSelf()) && "yes".equals(contactse.getIsSelf())){
                    contactse.setCustomerId(po.getCustomerId());
                    contactsMapper.insert(contactse);
                    rDeviceContacts.setIsSelf(contactse.getIsSelf());
                }
                rDeviceContacts.setContactsId(contactse.getId());
                rDeviceContactsMapper.insert(rDeviceContacts);
            }
        }
        return num;
    }

    @Transactional
    public long delete(Long id) throws InterruptedException {

        // 删除客户和设备的关联关系,递归级联删除
        List<DeviceDTO> deviceDTOs= selectDeviceBomDeep(id);
        deletePart(deviceDTOs);

        // 删除和附件的关联
        RFile rFile = new RFile();
        rFile.setDeviceId(id);
        rFileMapper.delete(rFile);

        // 删除设备
        Device po = new Device();
        po.setId(id);
        deviceMapper.delete(po);
        return 0;
    }
    public void deletePart(List<DeviceDTO> deviceDTO){
        if(deviceDTO.size()>0){
            for (int i = 0; i < deviceDTO.size(); i++) {
                //删除改设备的部件
                List<DeviceDTO> list = deviceDTO.get(i).getChildren();
                deletePart(list);
                // 删除bom
                DeviceBom deviceBom = new DeviceBom();
                deviceBom.setDeviceId(deviceDTO.get(i).getId());
                deviceBomMapper.delete(deviceBom);
                // 删除和附件的关联
                RFile rFile = new RFile();
                rFile.setDeviceId(deviceDTO.get(i).getId());
                rFileMapper.delete(rFile);
                // 删除部件的所有测点
                DeviceCond deviceCond = new DeviceCond();
                deviceCond.setDeviceId(deviceDTO.get(i).getId());
                List<MonitorDTO> monitorDTOs = monitorService.selectPointsCopy(deviceCond);
                for (MonitorDTO monitorDTO : monitorDTOs) {
                    monitorService.deleteDevicePointCopy(monitorDTO.getRid());
                }

                //删除该设备
                Device po = new Device();
                po.setId(deviceDTO.get(i).getId());
                deviceMapper.delete(po);
            }
        }
    }


    public List<RFile> getFileUrls(Long id) {
        List<RFile> byCustomerId = rFileMapper.getByDeviceId(id,"device");
        for(RFile rFile:byCustomerId){
            rFile.setFileUrl(instanceClient.getGatewayUrl() + Paths.get("/static/", rFile.getFileUrl()).toString());
        }
        return byCustomerId;
    }

    public List<DeviceDTO> getDeviceTypeCount(DeviceCond cond) {
        List<DeviceDTO> deviceGroupDTOS = Lists.newArrayList();

        String typeTemp = "";
        Integer count = 0;
        Integer alarmCount = 0;
        Integer offlineCount = 0;
        DeviceDTO deviceDTO = new DeviceDTO();

        List<Device> devices = deviceMapper.selectDeviceTypeStatusCount(cond);
        for (Device device : devices) {
            if (!typeTemp.equals(device.getType())) {
                typeTemp = device.getType();

                deviceDTO = new DeviceDTO();
                count = 0;
                alarmCount = 0;
                offlineCount = 0;
                deviceDTO.setType(device.getType());
                deviceGroupDTOS.add(deviceDTO);
            }

            count += device.getCount();
            deviceDTO.setCount(count);

            if (MyConstant.DEVICE_STATUS_FAULT.equals(device.getStatus()) || MyConstant.DEVICE_STATUS_ALARM.equals(device.getStatus())) {
                alarmCount += device.getCount();
                deviceDTO.setAlarmCount(alarmCount);
            }

            if (MyConstant.DEVICE_STATUS_OFFLINE.equals(device.getStatus())) {
                offlineCount += device.getCount();
                deviceDTO.setOnlineCount(count - offlineCount);
            }
        }

        return deviceGroupDTOS;
    }

    /**
     * 获取从收集数据开始的机器数量统计信息
     *
     * @return Count OnlineCount StartDate EndDate
     */
    public DeviceDTO getCount() {
        DeviceDTO dto = new DeviceDTO();

        dto.setCustomerCount(customerMapper.selectCount(null));

        Device countCond = new Device();
        countCond.setIsRoot(Constant.YES);
        Integer count = deviceMapper.selectCount(countCond);
        dto.setCount(count);

        Device cond = new Device();
        cond.setStatus(MyConstant.DEVICE_STATUS_OFFLINE);
        cond.setIsRoot(Constant.YES);
        dto.setOnlineCount(count - deviceMapper.selectCount(cond));

        dto.setStartDate(kmxService.getKmxStartTime() == null ? null : kmxService.getKmxStartTime().toLocalDate());
        dto.setEndDate(LocalDate.now());

        return dto;
    }

    /**
     * 根据条件获取一个机组
     *
     * @param cond 查询条件
     * @return {@code DeviceDTO}
     */
    public DeviceDTO selectDevice(DeviceCond cond) {
        Device device = deviceMapper.selectDevice(cond);

        return processDevice(device);
    }

    // ------------------------------------------------------------

    public List<DeviceDTO> selectDevices(Device cond) {
        // 判断用户是否是系统管理员
        cond.setUserId(getUserId(cond.getUserId()));
        List<Device> poList = deviceMapper.selectDevices(cond);
        List<DeviceDTO> dtoList = Lists.newArrayList();
        for (Device device : poList) {
            dtoList.add(processDevice(device));
        }

        return dtoList;
    }

    public List<DeviceDTO> selectDevicesWithoutCheckUser(Device cond) {
        List<Device> poList = deviceMapper.selectDevices(cond);
        List<DeviceDTO> dtoList = Lists.newArrayList();
        for (Device device : poList) {
            dtoList.add(processDevice(device));
        }

        return dtoList;
    }


    public Long getUserId(Long userId){
        // 判断用户是否是系统管理员
        if(userId!=null){
            RectList rolesByUser = sysClient.getRolesByUser(userId);
            List list = rolesByUser.getResult();
            if(list!=null && list.size()>0){
                for(int i=0;i<list.size();i++){
                    Map map = (Map)list.get(i);
                    if(map.get("enname")!=null && !map.get("enname").toString().equals("Administrator")){
                        return userId;
                    }else{
                        return null;
                    }
                }
            }
        }
        return null;
    }

    public List<DeviceDTO> selectDeviceByCustomer(DeviceCond cond) {
        List<Device> poList = deviceMapper.selectDeviceByCustomerId(cond);

        List<DeviceDTO> dtoList = Lists.newArrayList();
        for (Device device : poList) {
            dtoList.add(processDevice(device));
        }

        return dtoList;
    }

    private DeviceDTO processDevice(Device device) {
        DeviceDTO dto = OrikaMapperFactory.getMapper().map(device, DeviceDTO.class);
        if (!StringUtils.isBlank(device.getImageUrl())) {
            dto.setImageUrl(instanceClient.getGatewayUrl() + Paths.get("/static/", device.getImageUrl()).toString());
        }
        if (!StringUtils.isBlank(device.getOverviewUrl())) {
            dto.setOverviewUrl(instanceClient.getGatewayUrl() + Paths.get("/static/", device.getOverviewUrl()).toString());
        }

        TDevice tDevice = device.getTemplate();
        if (tDevice != null) {
            dto.setTDevice(getTBomDTO(tDevice));
        }

        dto.setDataSource(device.getCustomValue(Constant.DICT_TYPE_DATA_SOURCE));

        CustomerDTO customer = new CustomerDTO();
        customer.setId(device.getCustomerId());
        customer.setName(device.getCustomerName());
        dto.setContacts(device.getContacts());
        dto.setCustomer(customer);

        if(device.getFileList() != null && device.getFileList().size()>0){
            List<RFile> byCustomerId = device.getFileList();
            for(RFile rFile:byCustomerId){
                rFile.setFileUrl(instanceClient.getGatewayUrl() + Paths.get("/static/", rFile.getFileUrl()).toString());
            }
            dto.setFileList(byCustomerId);
        }
        return dto;
    }
    // ------------------------------------------------------------

    private BomDTO getTBomDTO(TDevice tDevice) {
        BomDTO bomDTO = new BomDTO();
        bomDTO.setId(tDevice.getId());
        bomDTO.setName(tDevice.getName());
        if (!StringUtils.isBlank(tDevice.getImageUrl())) {
            bomDTO.setImageUrl(instanceClient.getGatewayUrl() + Paths.get("/static/", tDevice.getImageUrl()).toString());
        } else {
            bomDTO.setImageUrl(instanceClient.getGatewayUrl() + "/static/template_default.png");
        }

        return bomDTO;
    }

    /**
     * 遍历 bom 树, 广度优先
     * <p>把所有 bom 存入 list
     */
    public List<DeviceDTO> selectDeviceBomWide(Long deviceId) throws InterruptedException {
        LinkedBlockingQueue<Long> deviceIdQueue = new LinkedBlockingQueue<>();
        List<DeviceDTO> bom = Lists.newArrayList();

        DeviceBom condSelf = new DeviceBom();
        condSelf.setDeviceId(deviceId);
        DeviceBom self = deviceBomMapper.selectOneBom(condSelf);

        if (self == null) {
            return bom;
        }

        DeviceDTO selfDTO = OrikaMapperFactory.getMapper().map(self.getDevice(), DeviceDTO.class);
        selfDTO.setParentId(0L);
        bom.add(selfDTO);

        DeviceBom cond = new DeviceBom();
        cond.setParentId(deviceId);
        List<DeviceBom> level1s = deviceBomMapper.selectBom(cond);
        wideInner(level1s, deviceIdQueue, bom);

        while (!deviceIdQueue.isEmpty()) {
            DeviceBom innerCond = new DeviceBom();
            innerCond.setParentId(deviceIdQueue.take());
            List<DeviceBom> leafs = deviceBomMapper.selectBom(innerCond);

            wideInner(leafs, deviceIdQueue, bom);
        }

        return bom;
    }

    private void wideInner(List<DeviceBom> leafs, LinkedBlockingQueue<Long> deviceIdQueue, List<DeviceDTO> bom) throws InterruptedException {
        for (DeviceBom leaf : leafs) {
            deviceIdQueue.put(leaf.getDeviceId());

            DeviceDTO leafDTO = OrikaMapperFactory.getMapper().map(leaf.getDevice(), DeviceDTO.class);
            leafDTO.setParentId(leaf.getParentId());
            bom.add(leafDTO);
        }
    }

    /**
     * 遍历 bom 树, 递归, 深度优先
     */
    public List<DeviceDTO> selectDeviceBomDeep(Long deviceId) throws InterruptedException {
        List<DeviceDTO> bom = Lists.newArrayList();

        DeviceBom innerCond = new DeviceBom();
        innerCond.setDeviceId(deviceId);
        List<DeviceBom> selfPOs = deviceBomMapper.selectBom(innerCond);
        DeviceBom selfPO = selfPOs.get(0);

        DeviceDTO self = OrikaMapperFactory.getMapper().map(selfPO.getDevice(), DeviceDTO.class);
        self.setKey(selfPO.getDevice().getId());
        self.setParentId(0L);
        self.setRootId(deviceId);
        self.setChildren(Lists.newArrayList());

        bom.add(self);

        bom(deviceId,deviceId, self.getChildren());

        return bom;
    }

    public void bom(Long deviceId,Long parentId, List<DeviceDTO> children) {
        DeviceBom innerCond = new DeviceBom();
        innerCond.setParentId(parentId);
        List<DeviceBom> leafs = deviceBomMapper.selectBom(innerCond);
        for (DeviceBom leaf : leafs) {
            DeviceDTO inner = OrikaMapperFactory.getMapper().map(leaf.getDevice(), DeviceDTO.class);

            inner.setId(leaf.getDevice().getId());
            inner.setParentId(parentId);
            inner.setRootId(deviceId);
            inner.setChildren(Lists.newArrayList());
            children.add(inner);

            bom(deviceId,inner.getId(), inner.getChildren());
        }
    }

    /**
     * 插入设备, 有 tDeviceId 就从模板生成 , 有 groupId 就关联 group
     */
    @Transactional
    public Long insertDevice(Device po) throws InterruptedException, K2ResponseException {
        if (po.getParentId() == null || po.getParentId() == 0L) {
            po.setIsRoot(Constant.YES);
        } else {
//            K2Assert.existDeviceId(po.getParentId());
            po.setIsRoot(Constant.NO);
            return insertPart(po);
        }

        Long deviceId;

        if (po.getStatus() == null) {
            po.setStatus(MyConstant.DEVICE_STATUS_OFFLINE);
        }

        if (po.getTDeviceId() != null) {
            deviceId = template2Device(po);
            po.setId(deviceId);
        } else {
            deviceMapper.insertSelective(po);
            deviceId = po.getId();
        }

        if (!StringUtils.isBlank(po.getDataSource())) {
            DeviceCustomColumn deviceCustomColumnCond = new DeviceCustomColumn();
            deviceCustomColumnCond.setDeviceId(deviceId);
            deviceCustomColumnCond.setName("dataSource");
            deviceCustomColumnCond.setNameEn("dataSource");

            DeviceCustomColumn deviceCustomColumn = deviceCustomColumnMapper.selectOne(deviceCustomColumnCond);

            if (deviceCustomColumn != null) {
                deviceCustomColumn.setValue(po.getDataSource());
                deviceCustomColumnMapper.updateByPrimaryKeySelective(deviceCustomColumn);
            } else {
                deviceCustomColumnCond.setValue(po.getDataSource());
                deviceCustomColumnMapper.insertSelective(deviceCustomColumnCond);
            }
        }

        // 关联客户
        if (po.getCustomerId() != null) {
            K2Assert.existCustomerId(po.getCustomerId());
            RDeviceCustomer rPo = new RDeviceCustomer();
            rPo.setDeviceId(deviceId);
            rPo.setCustomerId(po.getCustomerId());
            rDeviceCustomerMapper.insertSelective(rPo);
        }

        // 关联附件
        List<RFile> rFiles = RFile.updateFiles(deviceId,po.getFileList(),"device");
        if(rFiles!=null && rFiles.size()>0){
            rFileMapper.insertBatch(rFiles);
        }

        // 联系人
        if(po.getContacts() != null && po.getContacts().size()>0){
            for(Contacts contactse: po.getContacts()){
                RDeviceContacts rDeviceContacts = new RDeviceContacts();
                rDeviceContacts.setDeviceId(po.getId());
                rDeviceContacts.setCustomerId(po.getCustomerId());
                // 判断联系人是否是自己添加的
                if(!StringUtils.isEmpty(contactse.getIsSelf()) && "yes".equals(contactse.getIsSelf())){
                    contactsMapper.insert(contactse);
                    rDeviceContacts.setIsSelf(contactse.getIsSelf());
                }
                rDeviceContacts.setContactsId(contactse.getId());
                rDeviceContactsMapper.insert(rDeviceContacts);
            }
        }
        return deviceId;
    }

    /**
     * 插入设备部件
     */
    @Transactional
    public Long insertPart(Device device) throws InterruptedException, K2ResponseException {
        deviceMapper.insertSelective(device);
        // bom 关联表
        DeviceBom bom = new DeviceBom();
        bom.setDeviceId(device.getId());
        bom.setParentId(device.getParentId() == null ? 0L : device.getParentId());
        deviceBomMapper.insertSelective(bom);
        return device.getId();
    }

    // ================================================================
    // 从模板生成设备记录 begin
    // ================================================================
    /**
     * 从模板生成设备数据
     */
    private Long template2Device(Device po) throws K2ResponseException, InterruptedException {
        Long tDeviceId = po.getTDeviceId();

        TDevice tDevice = tDeviceMapper.selectByPrimaryKey(tDeviceId);

        // 主表
        Device device = OrikaMapperFactory.getMapper().map(po, Device.class);
        device.setIsRoot(Constant.YES);
        device.setType(tDevice.getType());
        deviceMapper.insertSelective(device);

        Long deviceId = device.getId();

        copyMonitor(tDeviceId, deviceId, deviceId);

        // 关联表
        RDeviceTemplateDevice rDeviceTemplateDevice = new RDeviceTemplateDevice();
        rDeviceTemplateDevice.setDeviceTemplateId(tDeviceId);
        rDeviceTemplateDevice.setDeviceId(deviceId);
        rDeviceTemplateDeviceMapper.insertSelective(rDeviceTemplateDevice);

        // bom 部件
        DeviceBom selfBom = new DeviceBom();
        selfBom.setDeviceId(deviceId);
        selfBom.setParentId(0L);
        deviceBomMapper.insertSelective(selfBom);

        TDevice cond = new TDevice();
        cond.setParentId(tDeviceId);
        List<BomDTO> tDeviceBomList = templateService.selectTDeviceBomDeep(cond);
        int index = 0;
        for (BomDTO item : tDeviceBomList) {
            index++;
            insertComponent(item, deviceId, index, deviceId);
        }
        return deviceId;
    }

    /**
     * 拷贝模板测点数据
     *
     * @param tDeviceId 模板 deviceId
     * @param deviceId deviceId
     */
    private void copyMonitor(Long tDeviceId, Long deviceId, Long rootId) {
        TRDeviceMonitorPoint cond = new TRDeviceMonitorPoint();
        cond.setDeviceId(tDeviceId);
        List<TRDeviceMonitorPoint> rList = trDeviceMonitorPointMapper.select(cond);
        for (TRDeviceMonitorPoint rSource : rList) {
            RDeviceMonitorPoint r = new RDeviceMonitorPoint();
            r.setDeviceId(deviceId);
            r.setMonitorPointId(rSource.getMonitorPointId());
            r.setRootId(rootId);
            rDeviceMonitorPointMapper.insertSelective(r);
        }

//        List<TMonitorPoint> tMonitorPointList = templateService.selectTMonitorPoint(tDeviceId);
//        for (TMonitorPoint tMonitorPoint : tMonitorPointList) {
//            MonitorPoint monitorPoint = new MonitorPoint();
//            monitorPoint.setSensorName(tMonitorPoint.getSensorName());
//            monitorPoint.setSensorType(tMonitorPoint.getSensorType());
//            monitorPoint.setNameEn(tMonitorPoint.getNameEn());
//            monitorPointMapper.insertSelective(monitorPoint);
//
//            RDeviceMonitorPoint r = new RDeviceMonitorPoint();
//            r.setDeviceId(deviceId);
//            r.setMonitorPointId(monitorPoint.getId());
//            rDeviceMonitorPointMapper.insertSelective(r);
//
//            for (TMonitorVariable tMonitorVariable : tMonitorPoint.getMonitorVariables()) {
//                MonitorVariable po = OrikaMapperFactory.getMapper().map(tMonitorVariable, MonitorVariable.class);
//                po.setId(null);
//                po.setMonitorPointId(monitorPoint.getId());
//                monitorVariableMapper.insertSelective(po);
//
//                for (TMonitorLimitValue tMonitorLimitValue : tMonitorVariable.getLimits()) {
//                    MonitorLimitValue limitValuePO = OrikaMapperFactory.getMapper().map(tMonitorLimitValue, MonitorLimitValue.class);
//                    limitValuePO.setId(null);
//                    limitValuePO.setVariableId(po.getId());
//                    monitorLimitValueMapper.insertSelective(limitValuePO);
//                }
//            }
//        }
    }

    /**
     * 递归插入部件记录和 bom 记录
     *
     * @param item 部件 pojo
     * @param parentId parentId
     */
    private void insertComponent(BomDTO item, Long parentId, Integer index, Long rootId) {
        Device component = new Device();
        component.setDeviceNo("COMPONENT" + parentId + index);
        component.setName(item.getName());
        component.setType(item.getType());
        component.setSpec(item.getSpec());
        component.setTDeviceId(item.getId());
        component.setIsRoot(Constant.NO);
        deviceMapper.insertSelective(component);
        Long deviceId = component.getId();

        DeviceBom deviceBom = new DeviceBom();
        deviceBom.setDeviceId(deviceId);
        deviceBom.setParentId(parentId);
        deviceBomMapper.insertSelective(deviceBom);

        // 测点
        copyMonitor(item.getId(), deviceId, rootId);

        for (BomDTO bomDTO : item.getChildren()) {
            index++;
            insertComponent(bomDTO, deviceId, index, rootId);
        }
    }
    // ================================================================
    // 从模板生成设备记录 end
    // ================================================================

    /**
     * 找到叶子的根节点 id
     */
    public Long selectRootDeviceId(Long leafId) {
        Long parentId = deviceMapper.selectParentId(leafId);

        if (new Long(0L).equals(parentId)) {
            return leafId;
        }

        return selectRootDeviceId(parentId);
    }


    public List<Contacts> getContactsByDevice(Long id) {
        List<Contacts> byDevice = contactsMapper.getByDevice(id);
        return byDevice;
    }

    public Map<String, Map<String,Integer>>  getDeviceStatusCount(Device device,String showType) {
        List<DeviceDTO> deviceDTOs = selectDevices(device);
        Map<String,Map<String,Integer>> result = Maps.newHashMap();
        Map<String, List<DeviceDTO>> datasource= deviceDTOs.parallelStream().collect(Collectors.groupingBy(DeviceDTO::getDataSource));
        datasource.forEach((k,v) -> {
            Map<String,Integer> status = Maps.newHashMap();
            Map<String, List<DeviceDTO>> collect = v.parallelStream().collect(Collectors.groupingBy(DeviceDTO::getStatus));
            if(showType.equals("allStatus")){
                DictRest statusTypes = sysClient.get(MyConstant.DICT_DEVICE_STATUS_TYPE);
                for (DictResult dictResult : statusTypes.getResult()) {
                    List<DeviceDTO> orDefault = collect.getOrDefault(dictResult.getLabel(), Lists.newArrayList());
                    status.put(dictResult.getLabel(),orDefault.size());
                }
            }else{
                collect.forEach((k1,v1) -> {
                    status.put(k1,v1.size());
                });
            }
            result.put(k,status);
        });
        if(device.getDataSource()==null){
            Map<String, List<DeviceDTO>> collect = deviceDTOs.parallelStream().collect(Collectors.groupingBy(DeviceDTO::getStatus));
            Map<String,Integer> status = Maps.newHashMap();
            if(showType.equals("allStatus")){
                DictRest statusTypes = sysClient.get(MyConstant.DICT_DEVICE_STATUS_TYPE);
                for (DictResult dictResult : statusTypes.getResult()) {
                    List<DeviceDTO> orDefault = collect.getOrDefault(dictResult.getLabel(), Lists.newArrayList());
                    status.put(dictResult.getLabel(),orDefault.size());
                }
            }else{
                collect.forEach((k1,v1) -> {
                    status.put(k1,v1.size());
                });
            }
            result.put("all",status);
        }
        return result;

    }

}
